/*
 * @Author: zhanyinjia
 * @Date: 2022-07-09 14:49:42
 * @LastEditTime: 2022-07-12 22:22:26
 * @LastEditors: zhanyinjia
 * @Description: 
 * @FilePath: \ts\1-base.ts
 */

// ts中的类型
//字符串 数字 布尔类型 元祖 数组 枚举 any null undefined never object对象类型

// 1、基础类型
// 数字 字符串 布尔类型
// 所有的类型 :后面的是类型  =后面的是值

let bool: boolean = true;
let num: number = 10;
let str: string = 'hello';

// 元祖 限制长度个数、类型一一对应，也是数组
let tuple: [string, number, boolean] = ['hello', 10, true];
// 向元组中增加数据，不能通过索引添加属性，并且只能增加元组中存放的类型
// tuple[0] = 100;
// tuple[3] = 'world';
tuple.push('world');
tuple.unshift(100);
// tuple.unshift(null);

// 数组 存放一类类型的集合
let arr1: number[] = [1, 2, 3];
let arr2: string[] = ['1', '2', '3'];

// 联合类型 可以看做是并集
let value: number | string = 10;
value = 'hello'
let arr3: (number | string)[] = [1, '2', 3];  // 既能使用数字，也能使用字符串
let arr4: Array<number | string> = [1, '2', 3, '4']; // 泛型方式来声明
// let arg5: any[] = [1, '2', true]  //什么都能放，不进行类型校验

// 枚举
let USER_ROLE = {
    USER: 0,
    ADMIN: 1,
    MANAGER: 2,
}

// 普通枚举 可以正举，也可以反举
enum USER_ROLE1 {
    USER, // 默认下标从0开始
    ADMIN,
    MANAGER
}
// console.log(USER_ROLE1.USER);
// console.log(USER_ROLE1[0]);
// console.log(USER_ROLE1);
// {0: "USER", 1: "ADMIN", 2: "MANAGER", USER: 0, ADMIN: 1, MANAGER: 2}

// 异构枚举 可以在枚举中放不同的值
enum USER_ROLE2 {
    USER = 'user',
    ADMIN = 1,
    MANAGER,
}
console.log(USER_ROLE2);
// {USER: 'user', ADMIN: 1, MANAGER: 2, 1: "ADMIN", 2: "MANAGER" }

// 常量枚举 只提供了一个类型
const enum USER_ROLE3 {
    USER,
    ADMIN,
    MANAGER,
}
// console.log(USER_ROLE3);
console.log(USER_ROLE3.USER);  // console.log(0 /* USER_ROLE3.USER */);

// any 不进行类型校验，尽量避免使用
let anyArr: any = ['hello', true, { name: 'world' }]
let r1 = anyArr[0]  //从any类型中拿出来的值默认也都是any
let r2 = arr1[0]

// null和undefind 
// 任何类型的子类型，如果tsconfig.json的strict的值为true，那么只能null赋值给null,undefined赋值给undefined

let str2: number | boolean;
// str2 = null;
let str3: null = null

// void 只能接受null，undefined，一般用于函数的返回值，严格模式下不能将null赋予给void
let v: void;
v = undefined;
// v = null;  //函数不写返回值时默认返回undefined，void默认兼容undefined

function a(): void {
    return undefined
    // return 123;
}

// never
// 任何类型的子类型,never代表不会出现的值。不能把其他类型赋值给never
// 永远达不到的3种情况：1.报错 2.死循环 3.类型推断时
function error(): never {
    throw new Error("err");
}
let n: never = error()

function loop(): never {
    while (true) { }
}

function fn1(x: number | string) {
    if (typeof x == 'number') {

    } else if (typeof x === 'string') {

    } else {
        console.log(x); // never
    }
}

function fn2(x: number | string | boolean) {
    if (typeof x == 'number') {
        x
    } else {
        x
    }
}

// 对象类型 非原始类型 object
let create = (obj: object): void => { }
// create(1);
create({});
create([]);
create(function () { })

// Q:什么时候标识类型？什么时候不用标识？
// ts自带类型推导的功能
let name1;   //声明不赋值的时候，默认是any
name1 = 'hello';
name1 = 100;

// 默认在初始化的时候会进行类型推导
let name2 = "hello";
// name2 = 100;

// Q:number和Number、string和String的区别
// 在使用基本数据类型时，会将原始类型包装成对象类型
11..toString();   // 11.0.toString() (11).toString()  Number(11).toString();
let number1: number = 11;
let number2: Number = 11;
let number3: number = Number(11);
let number4: Number = Number(11);
// let number5: number = new Number(11);  //不能把实例赋予给基本类型
// 类也是一个类型，它可以描述实例，number是基本数据类型，Number是它的封装类
let number6: Number = new Number(11);


// 联合类型
// 在使用联合类型时，没有赋值时只能访问联合类型中共有的方法和属性
let name3: string | number;
console.log(name3!.toString()); // 公共方法
// console.log(name3!.toFixed(2));
name3 = 10;
console.log(name3.toFixed(2));
name3 = 'hello';
console.log(name3.toLowerCase());

let ele: HTMLElement | null = document.getElementById('#app');
ele!.style.color = 'red'; // !表示非空，断定ele元素一定有值
(ele as HTMLElement).style.color = 'red';

// 类型断言
let name4: string | number;
(name4! as number).toFixed(2); // 强制

// 双重断言
// ele! as boolean;
((ele! as any) as boolean);
// 尽量不要使用双重断言，会破坏原有类型关系，断言为any是因为any类型可以被赋值给其他类型

// 字面量类型
let direction1: 'Up' | 'Down' | 'Left' | 'Right' = 'Down';
type Direction = 'Up' | 'Down' | 'Left' | 'Right';  //type这个关键字就是给类型起个别名
let direction2: Direction = 'Down';
// 可以用字面量当做类型，同时也表明只能采用这几个值（限定值），类似枚举。

export { }